﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows;

namespace SL8.SL
{
    /// <summary>
    /// 视图模型基类
    /// </summary>
    public abstract class ObservableObject : INotifyDataErrorInfo, INotifyPropertyChanged
    {
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
        public event PropertyChangedEventHandler PropertyChanged;
        private ICollection<ValidationResult> _validationErrors;
        private DependencyObject _tag;
        private Dictionary<string, object> _propertyValues;

        [Display(AutoGenerateField = false)]
        public virtual DependencyObject Tag
        {
            get
            {
                if (this._tag == null)
                {
                    this._tag = new TagDependencyObject();
                }

                return this._tag;
            }
        }

        [Display(AutoGenerateField = false)]
        public virtual bool HasErrors
        {
            get
            {
                return this.ValidationErrors.Any<ValidationResult>();
            }
        }

        [Display(AutoGenerateField = false)]
        public virtual ICollection<ValidationResult> ValidationErrors
        {
            get
            {
                if (this._validationErrors == null)
                {
                    this._validationErrors = new System.Collections.ObjectModel.ObservableCollection<ValidationResult>();
                }

                return this._validationErrors;
            }
        }

        private Dictionary<string, object> propertyValues
        {
            get
            {
                if (this._propertyValues == null)
                {
                    this._propertyValues = new Dictionary<string, object>();
                }

                return this._propertyValues;
            }
        }

        public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            IEnumerable<ValidationResult> enumerable;
            Func<ValidationResult, bool> predicate = null;
            Func<ValidationResult, bool> func2 = null;

            if (string.IsNullOrEmpty(propertyName))
            {
                if (predicate == null)
                {
                    predicate = delegate(ValidationResult e)
                    {
                        if (e.MemberNames.Any<string>())
                        {
                            return e.MemberNames.Contains<string>(propertyName);
                        }

                        return true;
                    };
                }

                enumerable = this.ValidationErrors.Where<ValidationResult>(predicate);
            }
            else
            {
                if (func2 == null)
                {
                    func2 = delegate(ValidationResult e)
                    {
                        return e.MemberNames.Contains<string>(propertyName);
                    };
                }
                enumerable = this.ValidationErrors.Where<ValidationResult>(func2);
            }

            return enumerable.ToArray<ValidationResult>();
        }

        public void ValidateProperty(string propertyName, object value)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName");
            }

            PropertyInfo property = base.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
            if (property == null)
            {
                throw new ArgumentException("找不到验证的属性", "propertyName");
            }

            this.RemoveValidationError(propertyName);
            ValidationContext validationContext = new ValidationContext(this, null, null);
            validationContext.MemberName = propertyName;
            List<ValidationResult> validationResults = new List<ValidationResult>();
            Validator.TryValidateProperty(value, validationContext, validationResults);

            foreach (var item in validationResults)
            {
                ((System.Collections.ObjectModel.ObservableCollection<ValidationResult>)this.ValidationErrors).Insert(0, item);
            }

            this.RaiseErrorsChanged(propertyName);
        }

        public void RemoveValidationError(string propertyName)
        {
            int length = this.ValidationErrors.Count - 1;
            for (int i = length; i >= 0; i--)
            {
                if (((System.Collections.ObjectModel.ObservableCollection<ValidationResult>)this.ValidationErrors)[i].MemberNames.Contains(propertyName))
                {
                    this.ValidationErrors.Remove(((System.Collections.ObjectModel.ObservableCollection<ValidationResult>)this.ValidationErrors)[i]);
                }
            }

            this.RaiseErrorsChanged(propertyName);
        }

        public void AddValidationError(string propertyName, string errorMessage)
        {
            ((System.Collections.ObjectModel.ObservableCollection<ValidationResult>)this.ValidationErrors).Insert(0, new ValidationResult(errorMessage, new string[] { propertyName }));
            this.RaiseErrorsChanged(propertyName);
        }

        public void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void RaiseErrorsChanged(string propertyName)
        {
            if (this.ErrorsChanged != null)
            {
                this.ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        public void SetCacheValue<TValue>(string propertyName, TValue value)
        {
            this.propertyValues[propertyName] = value;
        }

        public TReturnValue GetCacheValue<TReturnValue>(string propertyName, TReturnValue defaultValue, bool setAsInit = true)
        {
            if (!this.propertyValues.ContainsKey(propertyName))
            {
                if (setAsInit)
                {
                    this.SetCacheValue(propertyName, defaultValue);
                } 

                return defaultValue;
            }

            return (TReturnValue)this.propertyValues[propertyName];
        }

        public TReturnValue GetCacheValue<TReturnValue>(string propertyName, Func<TReturnValue> funcDefaultValue, bool setAsInit = true)
        {
            if (funcDefaultValue == null)
            {
                throw new ArgumentNullException("funcDefaultValue");
            }

            if (!this.propertyValues.ContainsKey(propertyName))
            {
                TReturnValue defaultValue = funcDefaultValue();
                if (setAsInit)
                {
                    this.SetCacheValue(propertyName, defaultValue);
                }

                return defaultValue;
            }

            return (TReturnValue)this.propertyValues[propertyName];
        }
    }
}
